/**
 * 
 */
package edu.serg.mbar.ucon.pep;

import org.apache.log4j.Logger;

import edu.serg.mbar.ucon.interfaces.AttributeUpdateManager;
import edu.serg.mbar.ucon.interfaces.AttributeReader;
import edu.serg.mbar.ucon.interfaces.ExpressionEvaluator;
import edu.serg.mbar.ucon.interfaces.PrimitiveActionsHandler;
import edu.serg.mbar.ucon.meta.RegisteredStrings;
import java.util.Vector;
//import edu.serg.mbar.ucon.pdp.PolicyEvaluator;
import edu.serg.utils.XMLUtils;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * 
 * processes the Primitive Actions elements found in UCON policy. the Primitive Actions mostly consist of PreUpdates and PostUpdates of attributes.
 * @author Sanaullah
 *
 */
public class PrimitiveActionsManager implements PrimitiveActionsHandler {

	static Logger log = Logger.getLogger(PrimitiveActionsManager.class);
	static XMLUtils xmlUtils = new XMLUtils();
	private AttributeUpdateManager attUpdateManager;
	private AttributeReader attReader;
	private ExpressionEvaluator exprEvaluator;
	private final static String POLICIES_DIR = RegisteredStrings.POLICIES_DIR;
	
	
	public PrimitiveActionsManager(AttributeReader atRdr,AttributeUpdateManager atUpdateMgr, ExpressionEvaluator exprEval)
	{
		attUpdateManager = atUpdateMgr;
		attReader = atRdr;
		exprEvaluator = exprEval;
		
	}
	
	
	private PrimitiveActionsManager()
	{
		
	}
	
	
//	/**
//	 *  This method assigns an AttributeUpdateManager to the Primitive Actions handler. 
//	 *  This AttributeUpdateManager will be used by the PrimitiveActionsHandler to perform attribute updates.
//	 *  
//	 *  @param atUpdateMgr
//	 *  	An instance of the class implementing the AttributeUpdateManager interface, will be used for performing attribute update actions.
//	 */
//	@Override
//	public void assignAttributeUpdateManager(AttributeUpdateManager atUpdateMgr) 
//	{
//		attUpdateManager=atUpdateMgr; //store the value of the argument to the private variable
//	}

	/**
	 * handles the primitive actions based on the primitive action elements specified in the policy
	 *
	 * @param subject
	 * 		The subject which is exercising a right on an object
	 * @param Object 
	 * 		The object which is being accessed by the subject
	 * @param right
	 * 		The right being exercised by the subject
	 * @param policyType
	 * 		The type of Policy e.g. PreUpdate, PostUpdate, OnUpdate
	 * @return
	 * 		Returns true if the primitive action is performed and false if the action is not performed
	 */
	@Override
	public boolean handlePrimitiveActions(String subject, String object, String right, String policyType) 
	{
		/*
		 * WHY ARE THE subject, object INCLUDED in the PARAMETERS? 
		 * the subject, object and right are included in the parameters because they will be needed in determining and reqtreiving the policy that is to be parsed and also in the attribute updates, 
		 * in attribute updates we will need to determine the target of the attribute it can be either the subject, the object being requested or the object of anyother object.
		 * so we will pass these arguments to the function that processes the primitive actions nodes and that function then passes these (subject, object) parameters to the function that handles the attribute updates.
		 * the attribute update function passes the (subject, object) parameters to the attribute reader to determine the target of the attribute, which is the object to which the attribute belongs this object can be the subject requesting the object, or the object being requested itself, or any other object( with the specified objectID)
		 */
		boolean result =false; //
		log.debug("Handling Primitive Actions [PolicyType=" + policyType + "], [Object=" + object + "], [Subject=" + subject +"]");
		String policyFile = POLICIES_DIR + object +"Policy.xml";
		String xPath ="//p:PolicySet/p:Policy[@PolicyType='" + policyType + "' and p:Target/p:Subjects/p:Subject/text()='" + subject + "' and p:Target/p:Objects/p:Object/text()='"+ object +"']/p:PrimitiveActions";
		log.debug("Retreiving the primitive actions nodes with the help of xPath = " + xPath);
		try
		{
			NodeList primitiveActionsNodes = xmlUtils.xPathEvaluateToNodeList(xmlUtils.parseXmlFromFile(policyFile), xPath);
			int totalPrimitiveActionElements = primitiveActionsNodes.getLength();
			log.debug("The total number of primitive actions nodes retreived is " + totalPrimitiveActionElements);
			for (int i=0 ; i<totalPrimitiveActionElements ; i++)
			{
				result = processPrimitiveAction(primitiveActionsNodes.item(i), subject, object);
			}
		}
		catch(Exception e)
		{
			log.debug("An error occured while Handling primitive actions");
			e.printStackTrace();
		}
		
		return result;
	}
	
	private boolean processPrimitiveAction(Node primitiveAction, String subject, String object)
	{
		log.debug("Started processing Primitive Action Node");
		boolean result=false;
		NodeList childNodes = primitiveAction.getChildNodes();
		int totalChildNodes = childNodes.getLength();
		for (int i=0 ; i<totalChildNodes ; i++)
		{
			Node child = childNodes.item(i);
			String nodeName = child.getNodeName();
			if (nodeName.contains(":"))
			{
				nodeName = nodeName.split(":",2)[1];
			}
			
			if (nodeName.equals("AttributeUpdate"))
			{
				result = processAttributeUpdateNode(child, subject, object);
			}                                               
		}
		return result;
	}
	
	private boolean processAttributeUpdateNode(Node attributeUpdateNode, String subjectID, String objectID)
	{
		log.debug("Started processing Attribute update Node");
		boolean result = false;
		//get the expression Node
		Node expressionNode = xmlUtils.xPathEvaluateToNode(attributeUpdateNode, "./p:Expression");
		//evaluate the expression Node 
		Vector<Object> resultOfExpression;
		resultOfExpression = exprEvaluator.evaluateExpression(expressionNode, subjectID, objectID);
		
		Node targetAttributeNode = xmlUtils.xPathEvaluateToNode(attributeUpdateNode, "./p:TargetAttribute");
		
		String targetObjectID = attReader.resolveTargetOfObjectAttributeNode(targetAttributeNode, subjectID, objectID);
		
		String attributeName = nameOfAttribute(targetAttributeNode);
		
		attUpdateManager.updateAttributeValue(targetObjectID, attributeName, resultOfExpression);
		
//		NodeList childNodes = attributeUpdateNode.getChildNodes();
//		int totalChildNodes = childNodes.getLength();
//		for (int i=0 ; i<totalChildNodes ; i++)
//		{
//			Node child = childNodes.item(i);
//			//determine the name of the node
//			String nodeName = child.getNodeName();
//			//if the node contains a prefix then remove the prefix
//			if (nodeName.contains(":"))
//			{
//				nodeName = nodeName.split(":",2)[1];
//			}
//					
//			//if the node is a [TargetAttribute] element then resolve the target
//			if (nodeName.equalsIgnoreCase("TargetAttribute"))
//			{
//				//TODO: resolve the target of the attribute here	
//				String objectID = attReader.resolveTargetOfObjectAttributeNode(child, subject, object);
//				String nameOfAttribute = nameOfAttribute(child);
//			}
//		}
		return result;
	}
	
	private String nameOfAttribute(Node targetAttributeNode)
	{
		log.debug("Determining the Name of the Attribute from the TargetAttribute");
		String nameOfAttribute="";
		NodeList childNodes = targetAttributeNode.getChildNodes();
		int totalChildNodes = childNodes.getLength();
		for (int i=0; i<totalChildNodes ; i++)
		{
			Node child = childNodes.item(i);
			String nodeName = child.getNodeName();
			//if the nodeName contains a prefix then
			if (nodeName.contains(":"))
			{
				//Remove the prefix by splitting the name into two and assigning the second part to the nodeName
				nodeName = nodeName.split(":",2)[1];
			}
			if (nodeName.equalsIgnoreCase("AttributeName"))
			{
					if (child.hasChildNodes())
					{
						Node textNode = child.getFirstChild();
						nameOfAttribute = textNode.getNodeValue();
					}
			}
			
		}
		log.debug("The name of the target attribute is '" + nameOfAttribute +"'");
		return nameOfAttribute;
	}

}
